package deformation;

import java.util.ArrayList;
import java.util.Observable;
import java.util.Observer;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

import stegru.tools.multiThreat.ParallelFor;
import grid.Grid;
import grid.GridBuilder;
import grid.GridPoint;
import handler.Handler;

public abstract class AbstractDeformation_old<T extends PreCompute> implements Deformation, Observer{
	
	private Grid grid;
	private Grid movedGrid;
	private T[] precomputed;
	private boolean destroyed =false;
	private Handler handler;
	private long deformationStatus =0;
	private  ArrayList<DeformationListener> deformationListenerList = new ArrayList<DeformationListener>();
	private Executor executor = Executors.newCachedThreadPool();
	
	
	public void addDeformationListener(DeformationListener listener){
		destroyedCheck();
		deformationListenerList.add(listener);
	}
	
	public synchronized Grid calculate(){
		destroyedCheck();
		if (grid == null) {
			System.err.println("call initial(grid) bevore calculate");
			return null;
		}		
		if (this.deformationStatus < handler.getDeformationStatus()) {
			this.deformationStatus = handler.getDeformationStatus();
			this.movedGrid =  calculate_intern();
		}		
		return this.movedGrid;
	}
	
	private Grid calculate_intern() {
		final GridBuilder gBuild = grid.getBuilder();
			(new ParallelFor(0, grid.size()-1) {
				@Override
				protected void loop(int i) {
					gBuild.set(i, calculateGridPoint(grid.get(i), precomputed[i]));
				}
			}).calculate();		
		return gBuild.generate();
	}
	
	public abstract GridPoint calculateGridPoint(GridPoint v, T precomputed);
	
	public synchronized void destroy(){
		grid = null;
		movedGrid = null;
		precomputed = null;
		handler.deleteObserver(this);
		handler = null;
		deformationListenerList.clear();
		deformationListenerList = null;
		executor = null;
		destroyed = true;
	}

	private void destroyedCheck() {
		if (destroyed) {
			throw new NullPointerException("Deformation is destroyed");
		}
	}
	
	@Override
	protected void finalize() throws Throwable {
		destroy();
		super.finalize();
	}
	
	public abstract Handler getHandler();
	
	public boolean hasChanged(){
		destroyedCheck();
		if (grid == null) {
			throw new NullPointerException("Deformation isn't  initiated");
		}
		
		return this.deformationStatus < handler.getDeformationStatus();
	}
	

	public synchronized boolean initial(final Grid grid){
		destroyedCheck();
		if (this.grid != null) {
			return false;
		}
		this.grid = grid;	
		this.precomputed =  (T[]) new PreCompute[grid.size()];
		(new ParallelFor(0, grid.size()-1) {
			@Override
			protected void loop(int i) {
				precomputed[i] = preComputeGridPoint(grid.get(i));
			}
		}).calculate();
		this.handler = getHandler();
		this.handler.addObserver(this);
		return true;
	}
	
	public Deformation newInstance() {
		throw new UnsupportedOperationException();
	}

	public abstract T preComputeGridPoint(GridPoint v);
	
	public void removeDeformationListener(DeformationListener listener){
		destroyedCheck();
		deformationListenerList.remove(listener);
	}
	
	@Override
	public void update(Observable o, Object arg) {
			for (final DeformationListener deformationListener : deformationListenerList) {
				executor.execute(new Runnable() {
					@Override
					public void run() {
						deformationListener.deformationChanged();
					}
				});
				
			}
	}
	

}
